package com.jack.lib.net

import android.util.Log
import com.blankj.utilcode.util.NetworkUtils
import com.jack.lib.core.ext.trues
import com.jack.lib.core.net.*
import com.jack.lib.core.utils.JLog
import com.jack.lib.net.cache.NetCache
import com.jack.lib.net.retry.Retry
import com.jack.lib.net.throwable.RepeatException
import io.reactivex.Observable
import io.reactivex.ObservableTransformer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

/**
 * author : jack(黄冲)
 * e-mail : 907755845@qq.com
 * create : 2020/7/28
 * desc   :
 */
object JNet {

    private val retry: Retry by lazy { Retry.default() }

    init {
        NetworkUtils.registerNetworkStatusChangedListener(NetworkStatusObserver { retry.retry() })
    }

/*    fun <T, J: JBaseData<T>> net(api: Observable<J>, param: JNetParam<T, J> = JNetParam.build {  }){
        if (param.dispatchMode == NetDispatchMode.SINGLE && NetCache.contain(api.hashCode())) {
            return
        }
        api.compose(main())
            .doOnSubscribe {
                param.loading.create()
                param.cache.read {
                    param.loading.cancel()
                    param.onNext.invoke(it as T)
                    param.onComplete()
                }
            }
            .retryWhen(RetryWithDelay(0))
            .`as`(AutoDispose.autoDisposable(
                AndroidLifecycleScopeProvider
                    .from(param.view.lifecycle(), Lifecycle.Event.ON_DESTROY))
            )
            .subscribe({
                param.loading.cancel()
                NetCache.remove(api.hashCode())
                if (it.isSuccess()) {
                    param.onNext(it.data())
                    param.onComplete()
                    param.cache.write(it.data())
                }else{
                    param.onFail(it)
                    param.onComplete()
                    param.view.onFill(it)
                }
            }, {
                param.loading.cancel()
                retry.catchThrowableAdd(it, param)
                param.onError(it)
                param.view.onError(it)
                param.onComplete()
                JLog.e("jhttp", Log.getStackTraceString(it))
            })

    }*/

    private fun <T> main(): ObservableTransformer<T, T> {
        return ObservableTransformer { upstream: Observable<T> ->
            upstream.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
        }
    }

    @OptIn(FlowPreview::class)
    suspend fun <T, J: JBaseData<T>> net(param: JNetParam<T, J> = JNetParam.build {  }): JNetParam<T, J> {
        return withContext(Dispatchers.IO) {
            param.ktApi.asFlow().onStart {
                withContext(Dispatchers.Main) {
                    param.loading.create()
                    param.cache.read<T> {
                        param.loading.cancel()
                        param.onNext.invoke(it)
                    }
                }
            }.onCompletion {
                withContext(Dispatchers.Main) {
                    param.loading.cancel()
                    NetCache.remove(param.ktApi.hashCode())
                    param.onComplete()
                }
            }.catch {
                param.result = NetResult.Error(it)
            }.collect {
                if (it.isSuccess()) {
                    param.result = NetResult.Success(it.data())
                }else{
                    param.result = NetResult.Failure(it)
                }
            }
            param
        }
    }

    suspend fun <T, J: JBaseData<T>> networkHandler(param: JNetParam<T, J> = JNetParam.build {  }) {
        withContext(Dispatchers.Main) {
            if (param.dispatchMode == NetDispatchMode.SINGLE && NetCache.contain(param.ktApi.hashCode())) {
                param.result = NetResult.Cancel()
                return@withContext param.onCancel()
            }
            when (val result = net(param).result) {
                is NetResult.Success -> {
                    param.onNext(result.data)
                    param.cache.write(result.data)
                }
                is NetResult.Failure -> {
                    param.onFail(result.data)
                    param.catchError.trues { param.view.onFill(result.data) }
                }
                is NetResult.Error -> {
                    if (result.throwable is RepeatException) {
                        return@withContext param.onCancel()
                    }
                    retry.catchThrowableAdd(result.throwable, param)
                    param.onError(result.throwable)
                    param.catchError.trues { param.view.onError(result.throwable) }
                    JLog.e(JNetManager.TAG, Log.getStackTraceString(result.throwable))
                }
            }
        }
    }
}